#ifndef _COMM_HPP_
#define _COMM_HPP_

#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <sys/shm.h>
#include <sys/ipc.h>
using namespace std;

#define PATHNAME "."
#define PROJ_ID 0x66
// 共享内存的大小,一般建议是4KB的整数倍
// 系统分配共享内存是以4KB为单位的 -> 内存划分内存块的基本单位
#define MAX_SIZE 4096 // 如果这里为4097,内核会向上取整,但是如果通过ipcs -m查看会发现是4097
// 因为内核给你的,和自己能用的是两回事,给了2*4096,但是只能使用4097

// 共享内存的特点:
// 优点: 所有进程间通信,速度最快的,能大大的减少数据的拷贝次数
// 缺点: 没有进行同步和互斥的操作的,没有对数据做任何保护

// ipcs -m/q/s -> 可查看共享内存/消息队列/信号源信息
// ipcrm -m 共享内存id ->来删除共享内存
// 共享内存的生命周期是随OS的,不是随进程的 -> system V的特性

// 信号量 -> 本质是一个计数器,通常用来表示公共资源中,资源数量的多少问题的
// 公共资源 -> 被多个进程同时可以访问的数据
// 1.访问没有保护的公共资源 -> 数据不一致问题
// 2.我们未来将被保护起来的公共资源 -> 临界资源,大部分的资源是独立的
// 3.进程有对应的代码来访问这部分临界资源 -> 临界区, 其他的为非临界区
// 4.如何保护 -> 互斥&&同步
// 5.要么不做,要么做完,两态的这种情况 -> 原子性
// 信号量的PV操作 -> P操作: sem-- 预定资源 V操作: sem++ 释放资源
// 二元信号量 -> 互斥操作(信号量值为1)

key_t getKey()
{
    key_t k = ftok(PATHNAME, PROJ_ID);// 通过宏定义传入相同的参数,来获得相同的key
    if(k < 0)
    {
        // cin, cout, cerr -> stdin, stdout, stderr -> 0, 1, 2
        cerr << errno << ":" << strerror(errno) << endl;// strerror -> 错误码转化为错误描述
        exit(1);
    }

    return k;
}

int getShmHelper(key_t k, int flags)
{
    // k是要shmget,设置进共享内存属性中的
    // 用来表示该共享内存,在内核中的唯一性
    // shmid是进程在应用层使用表示唯一性

    int shmid = shmget(k, MAX_SIZE, flags);
    if(shmid < 0)
    {
        cerr << errno << ":" << strerror(errno) << endl;
        exit(2);
    }

    return shmid;
}

int getShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT);// IPC_CREAT -> 如果不存在就创建,存在就获取
}

int creatShm(key_t k)
{
    return getShmHelper(k, IPC_CREAT | IPC_EXCL | 0600);// IPC_EXCL -> 1.无法单独使用 2.IPC_CREAT | IPC_EXCL -> 如果不存在就创建,如果存在就出错返回    
    // 对于用户来说,如果创建成功,一定是一个新的shm(共享内存)
}

void *attachShm(int shmid)
{
    void *mem = shmat(shmid, nullptr, 0);// shmat将共享内存和进程产生关联
    if((long long)mem == -1L)// Linux -> 64系统,指针8字节
    {
        cerr << errno << ":" << strerror(errno) << endl;
        exit(3);
    }

    return mem;
}

void detachShm(void *start)
{
    if(shmdt(start) == -1)// shmdt去关联
    {
        cerr << "shmdt: " <<  errno << ":" << strerror(errno) << endl;
    }
}

void delShm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)// shmctl控制共享内存
    {
        cerr << errno << ":" << strerror(errno) << endl;
    }
}

#endif